#include "vcl_stl.h"

#include "bkbase.h"
#include "h_base.h"
#include "h_xml.h"
using namespace h_base;

#include "h_conv.h"
#include "bstcp.h"

#include "simbat.h"
using namespace simbat;

struct HClusterNodeInfo
{
   int Id;
   HString Addr;
   int Port;

   HClusterNodeInfo()
      : Id(-1), Port(-1)
   {}
};

const int COMM_TIME_OUT = 30000;

static const char *SimbatBase = NULL;
static char ErrMsg[8192 + 1];

static HString SyslogDir;
static HSyslog Syslog;
static int LocalPort;
static HString ShrDir;
static HString ConfigCustFile;                   // config_cust.xml路径

static bool ClusterFlag = false;                 // 是否集群
static HNodeRole MyRole = nrMaster;              // 本服务器角色（MASTER/SLAVE/BACKUP）
static int MyId = -1;                            // 本服务器ID（从0开始）
static vector<HClusterNodeInfo> ClusterNodeVec;  // 集群配置信息
static HCodeSet CodeSet = csUtf8;                // 字符集
static int MasterId = 0;
static bool HotbackEnableFlag = false;           // 是否启动热备
static bool HotbackPausedFlag = false;           // 是否暂停热备
static HString HotbackAddr;                      // 热备机IP地址
static int HotbackPort = -1;                     // 热备机端口

static void LoadFromCfg(HXmlTree &Cfg)
{
   SyslogDir = EnvEvaluate(Cfg.GetValue("SYSLOG_DIR", "$SIMBAT_BASE/etc"));
   Syslog.Setup(SyslogDir, Cfg.GetValueUpper("SYSLOG_LEVEL", "INFO"));

   if ( !GetMyId(SimbatBase, MyId, MyRole, ErrMsg) )
   {
      Syslog.Log(__FILE__, __LINE__, llError, "ONERR", ErrMsg);
      return;
   }

   LocalPort = StrToInt(Cfg.GetValue("PORT").c_str());
   ShrDir = EnvEvaluate(Cfg.GetValue("SHR_DIR", "$SIMBAT_BASE/shr"));

   {
      vector<HString> StrVec;
      Cfg.GetValues("CLUSTER", StrVec);
      ClusterFlag = StrVec.size() > 1;
      if ( ClusterFlag )
      {
         ClusterNodeVec.clear();
         for ( int i = 0; i < StrVec.size(); i++ )
         {
            HString &AddrStr = StrVec[i];
            vector<HString> AddrPort;
            SplitString(AddrStr.c_str(), ':', AddrPort, true);
            if ( AddrPort.size() != 2 && AddrPort.size() != 3 )
            {
               snprintf(ErrMsg, 8192, "Invalid cluster address(%s)", AddrStr.c_str());
               Syslog.Log(__FILE__, __LINE__, llError, "ONERR", ErrMsg);
               return;
            }
            HClusterNodeInfo Node;
            Node.Id = i;
            Node.Addr = AddrPort[0];
            Node.Port = StrToInt(AddrPort[1].c_str());
            if ( Node.Port <= 2048 )
            {
               snprintf(ErrMsg, 8192, "Port %s too small, must be greater than 2048", AddrStr.c_str());
               Syslog.Log(__FILE__, __LINE__, llError, "ONERR", ErrMsg);
               return;
            }
            ClusterNodeVec.push_back(Node);
         }
      }
      else
      {
         HClusterNodeInfo Node;
         Node.Id = 0;
         Node.Addr = "127.0.0.1";
         Node.Port = LocalPort;
         ClusterNodeVec.push_back(Node);
      }
   }

   {
      HotbackEnableFlag = ( Cfg.GetValueUpper("HOTBACK_ENABLE", "FALSE") == "TRUE" );
      HotbackPausedFlag = ( Cfg.GetValueUpper("HOTBACK_PAUSED", "FALSE") == "TRUE" );
      HString AddrStr = Cfg.GetValue("HOTBACK_ADDR");
      if ( AddrStr == "" )
      {
         HotbackEnableFlag = false;
         HotbackAddr = "";
         HotbackPort = -1;
      }
      else
      {
         vector<HString> AddrPort;
         SplitString(AddrStr.c_str(), ':', AddrPort, true);
         if ( AddrPort.size() == 2 )
         {
            HotbackAddr = AddrPort[0];
            HotbackPort = StrToInt(AddrPort[1].c_str());
         }
         else
         {
            HotbackEnableFlag = false;
            HotbackAddr = "";
            HotbackPort = -1;
         }
      }
   }
}

static void LoadFromCfgCust(HXmlTree &Cfg)
{
   CodeSet = StrToCodeSet(Cfg.GetValueUpper("CODE_SET", "UTF8"));
}

static bool LoadCfg(HString &ErrStr)
{
   char FileName[500 + 1];
   HXmlTree Cfg;
   HXmlTree CfgCust;

   {
      HFileLockRead Lock("$SIMBAT_BASE/tmp/config.lck");
      snprintf(FileName, 500, "%s/etc/config.xml", SimbatBase);
      if ( !Cfg.LoadFromFile(FileName) )
      {
         ErrStr = Cfg.GetErrorString();
         return false;
      }
      LoadFromCfg(Cfg);
      snprintf(FileName, 500, "%s/etc/config_cust.xml", ShrDir.c_str());
      ConfigCustFile = FileName;
      if ( FileExist(ConfigCustFile.c_str()) )
      {
         if ( !CfgCust.LoadFromFile(ConfigCustFile.c_str()) )
         {
            snprintf(ErrMsg, 2048, "Load %s fail, %s", ConfigCustFile.c_str(),
               CfgCust.GetErrorString().c_str());
            snprintf(ErrMsg, 2048, "Load config.xml fail, %s", CfgCust.GetErrorString().c_str());
            ErrStr = ErrMsg;
            return false;
         }
         LoadFromCfgCust(CfgCust);
      }
   }

   return true;
}

static const char *PVER = "SIMJIE0104";

static int Batch(const char *BatchDate, const char *Addr, const char *BatchName, int Step, const char *JobName)
{
   BkPrintError("Batch(Date = %s, Addr = %s, Name = %s, Step = %d)",
      BatchDate, Addr, BatchName, Step);

   vector<HString> AddrVec;

   SplitString(Addr, ',', AddrVec, true);

   for ( int i = 0; i < AddrVec.size(); i++ )
   {
      HString &IpPort = AddrVec[i];
      if ( strchr(IpPort.c_str(), ':') == NULL )
      {
         char TmpStr[500 + 1];
         snprintf(TmpStr, 500, "%s:%d", IpPort.c_str(), LocalPort);
         IpPort = TmpStr;
      }
   }

   int AddrIdx = 0;
   bool SwitchFlag = AddrVec.size() == 1;

   if ( Step != 0 )
   {
      goto step1;
   }

   {
      HString Result;
      for ( ; ; )
      {
         HString &IpPort = AddrVec[AddrIdx];
         char Buf[500 + 1];
         if ( BatchDate[0] == '\0' )
         {
            snprintf(Buf, 500, "1|%s||%s", CodeSetToStr(CodeSet).c_str(), BatchName);
         }
         else
         {
            snprintf(Buf, 500, "1|%s||%s|%s", CodeSetToStr(CodeSet).c_str(), BatchName, BatchDate);
         }
         BkPrintError("CallServer(%s, START_BATCH, %s)", IpPort.c_str(), Buf);
         if ( CallServer(Syslog, COMM_TIME_OUT, IpPort, "START_BATCH", "", Buf, true, Result) )
         {
            BkPrintError("CallServer successfully");
            break;
         }
         BkPrintError("CallServer fail, result = %s", Result.c_str());
         if ( SwitchFlag )
         {
            return 8;
         }
         AddrIdx = 1 - AddrIdx;
         SwitchFlag = true;
      }
   }

   {
      int i;
      HString Result;
      bool MasterDownFlag = false;
      char Buf[500 + 1];
      char AddrStr[200 + 1];
      snprintf(Buf, 500, "t|||%s", JobName);
      for ( i = 0; i < 60; i++ )
      {
         if ( MasterDownFlag && HotbackAddr != "" )
         {
            snprintf(AddrStr, 200, "%s:%d", HotbackAddr.c_str(), HotbackPort);
         }
         else
         {
            snprintf(AddrStr, 200, "%s:%d", ClusterNodeVec[MasterId].Addr.c_str(), ClusterNodeVec[MasterId].Port);
         }
         BkPrintError("CallServer(%s, STEP, %s)", AddrStr, Buf);
         if ( CallServer(Syslog, COMM_TIME_OUT, AddrStr, "STEP", "", Buf, true, Result) )
         {
            BkPrintError("CallServer successfully");
            break;
         }
         BkPrintError("CallServer fail, result = %s", Result.c_str());
         sleep(10);
         MasterDownFlag = !MasterDownFlag;
      }
      if ( i == 60 )
      {
         BkPrintError("Send STEP, retry %d times fail", i);
         return 8;
      }
   }

step1 :

   {
      int r;
      HString Result;
      bool SuccessFlag = false;
      for ( ; ; )
      {
         HString &IpPort = AddrVec[AddrIdx];
         HString ServerIp;
         int ServerPort;
         HRaw Raw;
         SplitIpPort(IpPort, ServerIp, ServerPort);
         int Sock = BsCreateClientSocket(ServerIp.c_str(), ServerPort, COMM_TIME_OUT);
         if ( Sock < 0 )
         {
            BkPrintError("Connect to(%s, %d) fail, r = %d, errno = %d",
               ServerIp.c_str(), ServerPort, Sock, errno);
            goto error;
         }
         char Buf[8192 + 1];
         snprintf(Buf, 500, "%sH|%s||", PVER, CodeSetToStr(CodeSet).c_str());
         r = BsWriteToSocketHSL(Sock, Buf, strlen(Buf), COMM_TIME_OUT);
         if ( r < 0 )
         {
            BkPrintError("BsWriteToSocketHSL fail, r = %d, errno = %d", r, errno);
            close(Sock);
            goto error;
         }
         r = BsReadFromSocketHSL(Sock, Buf, 8192, COMM_TIME_OUT);
         if ( r <= 0 )
         {
            BkPrintError("BsReadFromSocketHSL fail, r = %d, errno = %d", r, errno);
            close(Sock);
            goto error;
         }
         close(Sock);
         {
            HMemStream Stream(Buf, r);
            HXmlTree Xml;
            if ( !Xml.LoadFromStream(&Stream, true) )
            {
               BkPrintError("Xml.LoadFromStream fail, ErrStr = %s", Xml.GetErrorString().c_str());
               goto error;
            }
            bool XmlPropertyFlag = false;
            vector<string> Version;
            SplitString(Xml.GetValue("VERSION"), '.', Version);
            if ( Version.size() == 3 )
            {
               int Ver = StrToInt(Version[0].c_str()) * 256 + StrToInt(Version[1].c_str());
               XmlPropertyFlag = ( Ver >= 0x0204 );
            }
            HString BatchStatus;
            if ( XmlPropertyFlag )
            {
               BatchStatus = Xml.GetProperty("BATCH_STATUS");
            }
            else
            {
               BatchStatus = Xml.GetValue("BATCH_STATUS");
            }
            if ( BatchStatus == "STOPPED" )
            {
               return 0;
            }
         }
         sleep(5);
         continue;
error :
         if ( SwitchFlag )
         {
            return 8;
         }
         AddrIdx = 1 - AddrIdx;
         SwitchFlag = true;
      }
   }
}

void PrintUsage()
{
   fprintf(stderr, "Usage : [PATH]/simbat_batch [--date=batch_date] host_name:port[,host_name2:port2] batch_name step job_name\n");
}

int main(int argc, char *argv[])
{
   if ( strchr(argv[0], '/') != NULL )
   {
      SetBaseByPath(argv[0]);
   }

   if ( argc == 1 )
   {
      PrintUsage();
      return 8;
   }

   SimbatBase = getenv("SIMBAT_BASE");

   if ( SimbatBase == NULL )
   {
      fprintf(stderr, "$SIMBAT_BASE not defined\n");
      return 8;
   }

   HString ErrStr;

   if ( !LoadCfg(ErrStr) )
   {
      BkPrintError("Load config.xml fail, %s", ErrStr.c_str());
      PrintUsage();
      return 8;
   }

   int i = 1;
   const char *BatchDate = "";

   for ( ; i < argc; i++ )
   {
      if ( memcmp(argv[i], "--date=", 7) == 0 )
      {
         BatchDate = argv[i] + 7;
         continue;
      }
      if ( argv[i][0] == '-' )
      {
         PrintUsage();
         return 8;
      }
      else
      {
         break;
      }
   }

   if ( argc != i + 4 )
   {
      PrintUsage();
      return 8;
   }

   return Batch(BatchDate, argv[i], argv[i + 1], StrToInt(argv[i + 2]), argv[i + 3]);
}
